<template>
  <div class="codeMirrorContainer">
    <el-card>
      <div class="in-coder-panel" style="text-align: left">
        <el-select class="code-mode-select" v-model="mode" @change="changeMode">
          <el-option
            v-for="mode in modes"
            :key="mode.value"
            :label="mode.label"
            :value="mode.value"
          >
          </el-option>
        </el-select>
        <textarea class="textarea" ref="textarea"></textarea>
        <el-select class="theme-select" v-model="theme" @change="changeTheme">
          <el-option
            v-for="mode in themes"
            :key="mode.value"
            :label="mode.value"
            :value="mode.value"
          >
          </el-option>
        </el-select>
      </div>
    </el-card>
  </div>

</template>

<script type="text/ecmascript-6">
// 引入全局实例
import _CodeMirror from 'codemirror';
// 核心样式
import 'codemirror/lib/codemirror.css'

// 以下文件可按需引入
// 引入主题后还需要在 options 中指定主题才会生效
import 'codemirror/theme/blackboard.css';
import 'codemirror/theme/cobalt.css';
import "codemirror/theme/eclipse.css";
import "codemirror/theme/panda-syntax.css";
import"codemirror/theme/liquibyte.css"
import"codemirror/theme/paraiso-light.css"

// 需要引入具体的语法高亮库才会有对应的语法高亮效果
// codemirror 官方其实支持通过 /addon/mode/loadmode.js 和 /mode/meta.js 来实现动态加载对应语法高亮库
import 'codemirror/mode/clike/clike.js';  //java
// TODO 支持的编程语言中添加Python
import 'codemirror/mode/python/python.js';  //python

// 高亮行功能
import 'codemirror/addon/selection/active-line'
import 'codemirror/addon/selection/selection-pointer'

// 折叠
import 'codemirror/addon/fold/foldgutter.css'
import 'codemirror/addon/fold/foldcode'
import 'codemirror/addon/fold/foldgutter'
import 'codemirror/addon/fold/brace-fold'
import 'codemirror/addon/fold/comment-fold'

// 自动括号匹配功能
import 'codemirror/addon/edit/matchbrackets'
// 自动括号匹配功能
import 'codemirror/addon/edit/closebrackets'

// 获取全局实例
const CodeMirror = window.CodeMirror || _CodeMirror;

export default {
  name: 'in-coder',
  props: {
    // 外部传入的内容，用于实现双向绑定
    value: String,
    // 外部传入的语法类型
    language: {
      type: String,
      default: "java"
    }
  },
  data() {
    return {
      // 内部真实的内容
      code: '',
      // 默认的语法类型
      mode: '',
      theme: "blackboard",
      // 编辑器实例
      coder: null,
      // 默认配置
      options: {
        // 缩进格式
        tabSize: 2,
        // 主题，对应主题库 JS 需要提前引入
        theme: 'blackboard',
        // 显示行号
        lineNumbers: true,
        //代码折叠
        lineWrapping: true,
        foldGutter: true,
        gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'],
        line: true,
        // 软换行
        // 高亮行功能
        styleActiveLine: true,
        smartIndent: true, // 自动缩进
        matchBrackets: true,  //括号匹配
        autoCloseBrackets: true
      },
      // 支持切换的语法高亮类型，对应 JS 已经提前引入
      // 使用的是 MIME-TYPE ，不过作为前缀的 text/ 在后面指定时写死了
      // TODO 支持的编程语言中添加Python
      modes: [{
        value: 'x-java',
        label: 'Java',
        id: 2
      },
        {
          value: 'x-c',
          label: 'C',
          id: 0
        },
        {
          value: 'x-python',
          label: 'Python',
          id: 3
        },
        // TODO 支持的编程语言中添加C++
        {
          value: 'x-c++src',
          label: 'C++',
          id: 1
        }
      ],
      themes: [{
        value: 'blackboard',
      }, {
        value: 'eclipse',
      }, {
        value: 'panda-syntax',
      }, {
        value: 'liquibyte',
      }, {
        value: 'paraiso-light',
      }, {
        value: 'cobalt',
      }],
      //语言模板
      tp: {
        java: 'import java.util.Scanner;\n' +
          '\n' +
          'public class Main {\n' +
          '\n' +
          '    public static void main(String[] args) {\n' +
          '  \n' +
          '    }\n' +
          '}\n',
        c: '#include<stdio.h>\n' +
          '\n' +
          'int main() {\n' +
          '   // 在此注释后面编写代码\n\n' +
          '   return 0;\n' +
          '}',
        // TODO 支持的编程语言中添加Python
        python: '# Paste your code here\n',
        // TODO 支持的编程语言中添加C++
        cpp: '#include<iostream>\n' +
          '\n' +
          'using namespace std;\n' +
          '\n' +
          'int main() {\n' +
          '   // 在此注释后面编写代码\n\n' +
          '   return 0;\n' +
          '}'
      }
    }
  },
  mounted() {
    // 初始化
    this._initialize()
  },
  methods: {
    // 初始化
    _initialize() {
      // 初始化编辑器实例，传入需要被实例化的文本域对象和默认配置
      this.coder = CodeMirror.fromTextArea(this.$refs.textarea, this.options)
      this.code = this.tp.java;
      // 编辑器赋值
      this.coder.setValue(this.value || this.code)
      // 支持双向绑定
      this.coder.on('change', (coder) => {
        this.code = coder.getValue()
        if (this.$emit) {
          this.$emit('input', this.code)
        }
      })
      // 尝试从父容器获取语法类型
      if (this.language) {
        // 获取具体的语法类型对象
        let modeObj = this._getLanguage(this.language)

        // 判断父容器传入的语法是否被支持
        if (modeObj) {
          this.mode = modeObj.label;
          this.coder.setOption('mode', `text/${modeObj.value}`)
        }
      }
    },
    // 获取当前语法类型
    _getLanguage(language) {
      // 在支持的语法类型列表中寻找传入的语法类型
      return this.modes.find((mode) => {
        // 所有的值都忽略大小写，方便比较
        let currentLanguage = language.toLowerCase()
        let currentLabel = mode.label.toLowerCase()
        let currentValue = mode.value.toLowerCase()

        // 由于真实值可能不规范，例如 java 的真实值是 x-java ，所以讲 value 和 label 同时和传入语法进行比较
        return currentLabel === currentLanguage || currentValue === currentLanguage
      })
    },
    // 更改模式
    changeMode(val) {
      console.log(val)
      // 修改编辑器的语法配置
      this.coder.setOption('mode', `text/${val}`)
      if (val === 'x-c') {
        this.code = this.tp.c
      } else if (val === 'x-java') {
        this.code = this.tp.java
      } else if (val === 'x-c++src'){
        // TODO 支持的编程语言中添加C++
        this.code = this.tp.cpp
      }else {
        // TODO 支持的编程语言中添加Python
        this.code = this.tp.python
      }
      // 编辑器赋值
      this.coder.setValue(this.value || this.code)
      // 支持双向绑定
      this.coder.on('change', (coder) => {
        this.code = coder.getValue()
        console.log(this.code)
        if (this.$emit) {
          this.$emit('input', this.code)
        }
      })

      let id = this._getLanguage(val).id
      // 允许父容器通过以下函数监听当前的语法值
      this.$emit('language-change', id)

    },
    changeTheme(val) {
      // 修改编辑器的语法配置
      this.coder.setOption('theme', val)
    }

  }
}
</script>

<style>
.in-coder-panel {
  flex-grow: 1;
  display: flex;
  position: relative;
}

.cm-s-cobalt.CodeMirror {
  border-radius: 10px;
  box-shadow: 0 12px 20px 0 rgba(0, 0, 0, 0.1);
}

.CodeMirror {
  flex-grow: 1;
  z-index: 1;
}

.el-card__body {
  padding: 0 !important;
}

.CodeMirror-code {
  line-height: 19px;
}

.code-mode-select {
  position: absolute;
  z-index: 2;
  right: 150px;
  top: 10px;
  height: 100px;
  max-width: 130px;
}

.in-coder-panel .el-select-dropdown__item:hover {
  background-color: aqua;
}

.theme-select {
  position: absolute;
  z-index: 2;
  right: 10px;
  top: 10px;
  height: 100px;
  max-width: 130px;
}
</style>
